#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <fstream>
#include <sstream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unordered_map>
#include <openssl/md5.h>
#include <cstdio>
#include <sys/shm.h>
#include <sys/stat.h>
using namespace std;
using std::cout;
using std::endl;
using std::string;

std::string uname;
std::string password;
const std::string SERVER_IP = "106.54.221.174";
const int SERVER_PORT = 9090;
static std::string path = "/";

#define Die(code)   \
    do              \
    {               \
        exit(code); \
    } while (0)

#define DEBUG
#define CONV(v) (struct sockaddr *)(v)
enum
{
    USAGE_ERR = 1,
    SOCKET_ERR,
    BIND_ERR,
    CONNECT_ERR,
    OPEN_ERR
};

enum class Behavior
{
    REGISTER, // 注册
    LOGIN,    // 登录
    LS,
    CD,
    PWD,
    MKDIR,
    REMOVE,
    TOKEN, // 删除文件或非空文件夹
    GETS,  // 下载文件
    PUTS,  // 上传文件
    TRANSFER,
    OK,
    ERROR
};
bool CheckParase(const string op, shared_ptr<vector<string>> parase)
{
    if (op == "cd" && parase->size() != 0)
    {
        return true;
    }
    if (op == "pwd" && parase->size() == 0)
    {
        return true;
    }
    if (op == "puts" && parase->size() >= 3)
    {
        return true;
    }
    if (op == "gets" && parase->size() == 1)
    {
        return true;
    }
    if (op == "mkdir" && parase->size() == 1)
    {
        return true;
    }
    if (op == "rm" && parase->size() == 1)
    {
        return true;
    }
    if (op == "ls")
    {
        return true;
    }
    return false;
}
struct Protocol
{
    Behavior be_;
    int len_ = 0;
    char buf[4096];
};

Protocol Recv(int);

size_t getFileSize(string path)
{
    struct stat st{};
    int ret = stat(path.c_str(), &st);
    if (ret < 0)
    {
        perror("stat");
    }

    return st.st_size;
}

void verify(int sockfd)
{
    Protocol pro{};
    std::string req = uname + " " + password;
    pro.be_ = Behavior::LOGIN;
    pro.len_ = req.size();
    stpncpy(pro.buf, req.c_str(), pro.len_);
    send(sockfd, &pro, 4 + 4 + pro.len_, 0);

    Recv(sockfd);
}

bool IsComm(const std::string &cmd)
{
    static const std::unordered_map<std::string, Behavior> commandMap = {
        {"register", Behavior::REGISTER},
        {"login", Behavior::LOGIN},
        {"ls", Behavior::LS},
        {"cd", Behavior::CD},
        {"pwd", Behavior::PWD},
        {"mkdir", Behavior::MKDIR},
        {"rm", Behavior::REMOVE},
        {"token", Behavior::TOKEN},
        {"gets", Behavior::GETS},
        {"puts", Behavior::PUTS},
        {"transfer", Behavior::TRANSFER}};

    // 查找输入字符串是否在映射表中
    return commandMap.find(cmd) != commandMap.end();
};

shared_ptr<vector<string>> parseCmd(const string &line)
{
    shared_ptr<vector<string>> param = std::make_shared<vector<string>>();
    ;
    string tmp;
    for (char ch : line)
    {
        if (ch == ' ' && !tmp.empty())
        {
            param->emplace_back(tmp);
            tmp.clear();
        }
        else if (ch != ' ')
        {
            tmp += ch;
        }
    }

    if (!tmp.empty())
    {
        param->emplace_back(tmp);
    }
    return param;
}

int get_file_md5(const std::string &file_name, std::string &md5_value)
{
    md5_value.clear();

    std::ifstream file(file_name.c_str(), std::ifstream::binary);
    if (!file)
    {
        cout << "open file" << endl;
        return -1;
    }

    MD5_CTX md5Context;
    MD5_Init(&md5Context);

    char buf[1024 * 16];
    while (file.good())
    {
        file.read(buf, sizeof(buf));
        MD5_Update(&md5Context, buf, file.gcount());
    }

    unsigned char result[MD5_DIGEST_LENGTH];
    MD5_Final(result, &md5Context);

    char hex[35];
    memset(hex, 0, sizeof(hex));
    for (int i = 0; i < MD5_DIGEST_LENGTH; ++i)
    {
        sprintf(hex + i * 2, "%02x", result[i]);
    }
    hex[32] = '\0';
    md5_value = string(hex);
    return 0;
}

int *init_shm()
{
    int shmid = shmget(1000, 4096, IPC_CREAT | 0666);
    if (shmid < 0)
    {
        perror("shmget");
    }

    int *code = (int *)shmat(shmid, nullptr, O_WRONLY);
    if ((void *)code == (void *)-1)
    {
        perror("shmat");
    }
    *code = 0;
    return code;
}

int getCode()
{
    int shmid = shmget(1000, 4096, IPC_CREAT | 0666);
    if (shmid < 0)
    {
        perror("shmget");
    }

    int *code = (int *)shmat(shmid, nullptr, O_RDONLY);

    cout << "getcode:" << *code << endl;
    return *code;
}

void setCode(struct Protocol *pro, const string &path)
{
    static int *code = init_shm();
    if (pro && pro->be_ == Behavior::OK)
    {

        if (path == "..")
        {
            *code = *code - 1;
        }
        else if (path == "/")
        {
            *code = 0;
        }
        else
        {
            *code = *code + 1;
        }
    }
}
